/* Copyright 2005-2006 Tim Fennell
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.sourceforge.stripes.tag;

import net.sourceforge.stripes.exception.StripesJspException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTag;
import java.io.IOException;

/**
 * <p>Tag handler for a tag that produces an HTML label tag which is capable of looking up
 * localized field names and formatting the label when validation errors exist.  The field being
 * labelled is identified using either the {@code name} attribute (preferred) or the
 * {@code for} attribute.  If the {@code name} attribute is supplied this will always be used as
 * the lookup key (optionally pre-pended with the form's action path).  If the {@code name} field
 * is not supplied, the tag will fall back to using the value supplied for the {@code for}
 * attribute.  This is done because the {@code for} attribute is used by HTML as a reference to the
 * {@code id} of the input being labelled.  In the case where the id is equal to the field name
 * it is unnecessary to specify a {@code name} attribute for the label tag.  In cases where the
 * field name (or other localized resource name) does not match an HTML ID, the {@code name}
 * attribute must be used.</p>
 *
 * <p>The value used for the label is the localized field name if one exists.  Localized field
 * names are looked up in the field name resource bundle first using {@code formActionPath.fieldName},
 * and then (if no value is found) using just {@code fieldName}. If no localized String can be found
 * then the body of the label tag is used. If no body is supplied then a warning String will be used
 * instead!</p>
 *
 * @author Tim Fennell
 * @since Stripes 1.1
 */
public class InputLabelTag extends InputTagSupport implements BodyTag {

    private boolean nameSet;

    /** Sets the HTML ID of the field for which this is a label. */
    public void setFor(String forId) {
        set("for", forId);

        // If the name field isn't set yet, set it with the forId.
        if (!nameSet) {
            super.setName(forId);
        }
    }

    /** Gets the HTML ID of the field for which this is a label. */
    public String getFor() { return get("for"); }

    /**
     * Sets the name of the form element/label to be rendered. Should only be invoked by
     * the JSP container as it also tracks whether or not the container has set the name, in
     * order to correctly handle pooling.
     */
    public void setName(String name) {
        super.setName(name);
        this.nameSet = true;
    }

    /**
     * Does nothing.
     * @return EVAL_BODY_BUFFERED in all cases.
     */
    public int doStartInputTag() throws JspException {
        return EVAL_BODY_BUFFERED;
    }

    /** Does nothing. */
    public void doInitBody() throws JspException { /** Do Nothing */ }

    /**
     * Does nothing.
     * @return SKIP_BODY in all cases.
     */
    public int doAfterBody() throws JspException {
        return SKIP_BODY;
    }

    /**
     * Performs the main work of the tag as described in the class level javadoc.
     * @return EVAL_PAGE in all cases.
     * @throws JspException if an IOException is encountered writing to the output stream.
     */
    public int doEndInputTag() throws JspException {
        try {
            String label = getLocalizedFieldName();
            String fieldName = getAttributes().remove("name");

            if (label == null) {
                label = getBodyContentAsString();
            }

            if (label == null) {
                label = "Label could not find localized field name and had no body.";
            }

            // Write out the tag
            writeOpenTag(getPageContext().getOut(), "label");
            getPageContext().getOut().write(label);
            writeCloseTag(getPageContext().getOut(), "label");

            // Reset the field name so as to not screw up tag pooling
            if (this.nameSet) {
                super.setName(fieldName);
            }

            return EVAL_PAGE;
        }
        catch (IOException ioe) {
            throw new StripesJspException("Encountered an exception while trying to write to " +
                "the output from the stripes:label tag handler class, InputLabelTag.", ioe);
        }
    }

    /** Overridden to do nothing, since a label isn't really a form field. */
    protected void registerWithParentForm() throws StripesJspException { }

    /**
     * Wraps the parent loadErrors() to supress exceptions when the label is outside of a
     * stripes form tag.
     */
    protected void loadErrors() {
        try {
            super.loadErrors();
        }
        catch (StripesJspException sje) {
            // Do nothing, we're supressing this error
        }
    }
}
